package cn.zcltd.btg.set.core;

import cn.zcltd.btg.set.*;
import cn.zcltd.btg.set.exception.BtgSetRuntimerException;
import cn.zcltd.btg.set.util.StringUtil;
import cn.zcltd.btg.sutil.EmptyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.util.*;

/**
 * 模块，包含模块所有参数信息
 */
public class Module implements ToJson, ToXml, ParamGetter {
    private static final Logger log = LoggerFactory.getLogger(Module.class);

    private String id; //模块id（必须）
    private String name; //模块name
    private String desc; //模块描述
    private Config config; //模块所属配置（必须）

    private Map<String, Param> paramMap = new HashMap<>();//模块参数map
    private List<Param> params = new ArrayList<>(); //模块参数集合

    public Module(String id, String name, Config config) {
        if (EmptyUtil.isEmpty(id)) {
            throw new BtgSetRuntimerException("id must not be null");
        }
        if (null == config) {
            throw new BtgSetRuntimerException("config must not be null");
        }
        this.id = id;
        this.name = name;
        this.config = config;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public Config getConfig() {
        return config;
    }

    public void setConfig(Config config) {
        this.config = config;
    }

    public Map<String, Param> getParamMap() {
        return paramMap;
    }

    public void setParamMap(Map<String, Param> paramMap) {
        this.paramMap = paramMap;
    }

    public List<Param> getParams() {
        return params;
    }

    public void setParams(List<Param> params) {
        this.params = params;
    }

    /**
     * 为当前module创建一个param
     *
     * @param paramId    param id
     * @param paramName  param name
     * @param paramType  param type
     * @param paramValue param value
     * @return 被创建的param
     */
    public Param createParam(String paramId, String paramName, ParamType paramType, String paramValue) {
        Param param = new Param(paramId, paramName, paramType, paramValue, this);
        param.setSource(ParamSource.MODULE);
        log.debug("create param:" + param.getPath());
        this.addParam(param);
        return param;
    }

    /**
     * 添加一个参数
     *
     * @param param 需要添加的参数
     */
    public void addParam(Param param) {
        //若config全局配置存在此param，module中的param不允许重名
        Param paramInConfig = this.getConfig().getParam(param.getId());
        if (null != paramInConfig) {
            log.error("param exists in config:" + paramInConfig.getPath());
            return;
        }

        //若当前module中存在此param，更新param
        if (null != this.getParam(param.getId())) {
            log.warn("param exists:" + param.getPath());
            this.updateParam(param);
            return;
        }

        param.setModule(this); //module内部的param操作，全部指向当前module

        this.paramMap.put(param.getId(), param);
        this.params.add(param);

        this.getConfig().getParamMap().put(param.getId(), param); //将module中的参数添加到config param map中去

        log.debug("add param:" + param.getPath());
    }

    /**
     * 删除一个参数
     *
     * @param paramId 需要删除的参数id
     */
    public void deleteParam(String paramId) {
        if (null == this.paramMap.get(paramId)) {
            log.warn("param not exists:" + this.getConfig().getId() + ">" + this.getId() + ">" + paramId);
            return;
        }
        this.paramMap.remove(paramId);
        this.getConfig().getParamMap().remove(paramId); //从config参数map中去除该参数
        Iterator<Param> iterator = this.params.iterator();
        while (iterator.hasNext()) {
            Param param = iterator.next();
            if (param.getId().equals(paramId)) {
                iterator.remove();
                break;
            }
        }
        log.debug("delete param:" + this.getConfig().getId() + ">" + this.getId() + ">" + paramId);
    }

    /**
     * 更新一个参数
     *
     * @param param 需要更细的参数
     */
    public void updateParam(Param param) {
        param.setModule(this); //module内部的param操作，全部指向当前module

        Param paramOld = this.paramMap.get(param.getId());
        if (null == paramOld) {
            log.warn("param not exists:" + param.getPath());
            this.addParam(param);
            return;
        }
        if (paramOld.equals(param)) {
            log.debug("param no change:" + paramOld.getPath());
            return;
        }
        this.paramMap.put(param.getId(), param);
        for (int i = 0; i < this.params.size(); i++) {
            Param paramThis = params.get(i);
            if (paramThis.getId().equals(param.getId())) {
                this.params.remove(i);
                this.params.add(i, param);
                break;
            }
        }
        log.debug("update param:" + param.getPath());
    }

    /**
     * 获取一个参数
     *
     * @param paramId 参数id
     * @return 获取到的参数, 无参数返回null
     */
    public Param getParam(String paramId) {
        if (EmptyUtil.isEmpty(paramId)) {
            throw new BtgSetRuntimerException("paramId must not be null");
        }
        return this.paramMap.get(paramId.trim());
    }

    @Override
    public String toJson(boolean isFormat, int lv) {
        if (lv <= 0) {
            throw new BtgSetRuntimerException("lv must be greater than 1");
        }

        int tabNum = lv - 1;

        StringBuilder sb = new StringBuilder();

        if (isFormat) {
            sb.append(StringUtil.getTab(tabNum)).append("{");
            sb.append("\r\n");
            sb.append(StringUtil.getTab(tabNum + 1)).append("\"id\":\"").append(id).append("\"");
            sb.append(",\r\n");
            sb.append(StringUtil.getTab(tabNum + 1)).append("\"name\":\"").append(EmptyUtil.isNotEmpty(name) ? name : "").append("\"");
            sb.append(",\r\n");
            sb.append(StringUtil.getTab(tabNum + 1)).append("\"desc\":\"").append(EmptyUtil.isNotEmpty(desc) ? desc : "").append("\"");
            sb.append(",\r\n");
            sb.append(StringUtil.getTab(tabNum + 1)).append("\"params\":[");
            sb.append("\r\n");
            StringBuilder paramSb = new StringBuilder();
            for (Param param : params) {
                paramSb.append(param.toJson(true, tabNum + 3));
                paramSb.append(",\r\n");
            }
            if (paramSb.length() > 0) {
                paramSb.deleteCharAt(paramSb.length() - 3);
            }
            sb.append(paramSb);
            sb.append(StringUtil.getTab(tabNum + 1)).append("]");
            sb.append("\r\n");
            sb.append(StringUtil.getTab(tabNum)).append("}");
            return sb.toString();
        }

        sb.append("{");
        sb.append("\"id\":\"").append(id).append("\"");
        sb.append(",");
        sb.append("\"name\":\"").append(EmptyUtil.isNotEmpty(name) ? name : "").append("\"");
        sb.append(",");
        sb.append("\"desc\":\"").append(EmptyUtil.isNotEmpty(desc) ? desc : "").append("\"");
        sb.append(",");
        sb.append("\"params\":[");
        StringBuilder paramSb = new StringBuilder();
        for (Param param : params) {
            paramSb.append(param.toJson(false, 1));
            paramSb.append(",");
        }
        if (paramSb.length() > 0) {
            paramSb.deleteCharAt(paramSb.length() - 1);
        }
        sb.append(paramSb);
        sb.append("]");
        sb.append("}");
        return sb.toString();
    }

    @Override
    public String toJson() {
        return toJson(true, 1);
    }

    @Override
    public String toXml(boolean isFormat, int lv) {
        if (lv <= 0) {
            throw new BtgSetRuntimerException("lv must be greater than 1");
        }

        int tabNum = lv - 1;

        StringBuilder sb = new StringBuilder();

        if (isFormat) {
            sb.append(StringUtil.getTab(tabNum)).append("<module");
            sb.append(" id=\"").append(id).append("\"");
            if (EmptyUtil.isNotEmpty(name)) {
                sb.append(" name=\"").append(name).append("\"");
            }
            if (EmptyUtil.isNotEmpty(desc)) {
                sb.append(" desc=\"").append(desc).append("\"");
            }
            sb.append(">");
            for (Param param : params) {
                sb.append("\r\n");
                sb.append(param.toXml(true, tabNum + 2));
            }
            sb.append("\r\n");
            sb.append(StringUtil.getTab(tabNum)).append("</module>");

            return sb.toString();
        }

        sb.append("<module");
        sb.append(" id=\"").append(id).append("\"");
        if (EmptyUtil.isNotEmpty(name)) {
            sb.append(" name=\"").append(name).append("\"");
        }
        if (EmptyUtil.isNotEmpty(desc)) {
            sb.append(" desc=\"").append(desc).append("\"");
        }
        sb.append(">");
        for (Param param : params) {
            sb.append(param.toXml(false, 1));
        }
        sb.append("</module>");

        return sb.toString();
    }

    @Override
    public String toXml() {
        return toXml(true, 1);
    }

    @Override
    public String toString() {
        return this.toJson();
    }

    public boolean equals(Module target) {
        return this.toString().equals(target.toString());
    }

    /**
     * 获取module的配置path
     *
     * @return path
     */
    public String getPath() {
        return this.getConfig().getId() + ">" + this.getId();
    }

    @Override
    public String getString(String key) {
        return getString(key, null);
    }

    @Override
    public String getString(String key, String defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getString(param, defaultValue);
    }

    @Override
    public Boolean getBoolean(String key) {
        return getBoolean(key, null);
    }

    @Override
    public Boolean getBoolean(String key, Boolean defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getBoolean(param, defaultValue);
    }

    @Override
    public Number getNumber(String key) {
        return getNumber(key, null);
    }

    @Override
    public Number getNumber(String key, Number defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getNumber(param, defaultValue);
    }

    @Override
    public Integer getInteger(String key) {
        return getInteger(key, null);
    }

    @Override
    public Integer getInteger(String key, Integer defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getInteger(param, defaultValue);
    }

    @Override
    public Long getLong(String key) {
        return getLong(key, null);
    }

    @Override
    public Long getLong(String key, Long defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getLong(param, defaultValue);
    }

    @Override
    public Float getFloat(String key) {
        return getFloat(key, null);
    }

    @Override
    public Float getFloat(String key, Float defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getFloat(param, defaultValue);
    }

    @Override
    public Double getDouble(String key) {
        return getDouble(key, null);
    }

    @Override
    public Double getDouble(String key, Double defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getDouble(param, defaultValue);
    }

    @Override
    public BigDecimal getBigDecimal(String key) {
        return getBigDecimal(key, null);
    }

    @Override
    public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getBigDecimal(param, defaultValue);
    }

    @Override
    public Date getDate(String key) {
        return getDate(key, "");
    }

    @Override
    public Date getDate(String key, Date defaultValue) {
        return getDate(key, "", defaultValue);
    }

    @Override
    public Date getDate(String key, String datePattern) {
        return getDate(key, datePattern, null);
    }

    @Override
    public Date getDate(String key, String datePattern, Date defaultValue) {
        Param param = this.getParam(key);
        return ParamConvert.getDate(param, key, defaultValue);
    }
}